# Copyright (c) 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""This is a simplified Makefile generator for single-target gyp files.
It was originally designed for generating readable Makefiles for the
the NaCl examples.
"""

# pylint: disable=C0301

import os
import gyp.common
from gyp.common import GetEnvironFallback
from gyp.generator.make import QuoteIfNecessary

generator_default_variables = {
  'EXECUTABLE_PREFIX': '',
  'EXECUTABLE_SUFFIX': '',
  'STATIC_LIB_PREFIX': 'lib',
  'SHARED_LIB_PREFIX': 'lib',
  'STATIC_LIB_SUFFIX': '.a',
  'INTERMEDIATE_DIR': '$(BUILDDIR)/$(BUILDTYPE)/obj',
  'SHARED_INTERMEDIATE_DIR': '$(obj)/gen',
  'PRODUCT_DIR': '$(BUILDDIR)/$(BUILDTYPE)',
  'CONFIGURATION_NAME': '$(BUILDTYPE)',
}


generator_additional_non_configuration_keys = [
    'make_valid_configurations',
]


preamble = """\
#
# GNU Make based build file.  For details on GNU Make see:
#   http://www.gnu.org/software/make/manual/make.html
#
# This file was generated by gyp (http://code.google.com/p/gyp/)

# Default build configuration
BUILDTYPE = %(default_config)s

# All possible build configurations
BUILDTYPES = %(all_configs)s

# Check for valid build configuration
ifeq (,$(findstring $(BUILDTYPE),$(BUILDTYPES)))
$(warning Possible build configurations are: $(BUILDTYPES))
$(warning Cannot use BUILDTYPE=$(BUILDTYPE) with this Makefile.)
all:
else

# Target toolchain
CC.target ?= %(CC.target)s
CFLAGS.target ?= $(CFLAGS)
CXX.target ?= %(CXX.target)s
CXXFLAGS.target ?= $(CXXFLAGS)
LINK.target ?= %(LINK.target)s
LDFLAGS.target ?= $(LDFLAGS)
AR.target ?= %(AR.target)s
ARFLAGS.target ?= %(ARFLAGS.target)s

# Host toolchain
CC.host ?= gcc
CFLAGS.host ?=
CXX.host ?= g++
CXXFLAGS.host ?=
LINK.host ?= g++
LDFLAGS.host ?=
AR.host ?= ar
ARFLAGS.host := %(ARFLAGS.host)s

BUILDDIR = build

DEPFLAGS = -MMD

DEPFILES :=

.PHONY: all clean

all:

clean:
\trm -rf $(BUILDDIR)
"""

none_section = """
TARGET = $(BUILDDIR)/$(BUILDTYPE)/%(target)s.stamp
all: $(TARGET)

INPUTS = %(inputs)s

$(TARGET): $(INPUTS)
"""


target_section = """
TARGET = %(product)s
all: $(TARGET)

SOURCES = %(sources)s

LIBS_%(target_name_var)s_$(BUILDTYPE) = %(libs)s

OBJS = %(objs)s

DEPFILES += $(OBJS:%%.o=%%.d)

# Suffix rules, putting all outputs into build folder.
$(BUILDDIR)/$(BUILDTYPE)/obj_%(target)s/%%.o: %%.c
\t@mkdir -p $(dir $@)
\t$(CC.%(toolset)s) $(CFLAGS_%(target_name_var)s_$(BUILDTYPE)) -c -o $@ $<

$(BUILDDIR)/$(BUILDTYPE)/obj_%(target)s/%%.o: %%.cc
\t@mkdir -p $(dir $@)
\t$(CXX.%(toolset)s) $(CXXFLAGS_%(target_name_var)s_$(BUILDTYPE)) -c -o $@ $<
"""


lib_section = """
$(TARGET): $(OBJS)
\t@mkdir -p $(dir $@)
\t$(AR.%(toolset)s) $(ARFLAGS.%(toolset)s) $(ARFLAGS_%(target_name_var)s_$(BUILDTYPE)) $@ $^
"""


link_section = """
$(TARGET): $(OBJS)
\t@mkdir -p $(dir $@)
\t$(LINK.%(toolset)s) $(LDFLAGS_%(target_name_var)s_$(BUILDTYPE)) $(LDFLAGS.%(toolset)s) -o $@ -Wl,--start-group $^ $(LIBS_%(target_name_var)s_$(BUILDTYPE)) -Wl,--end-group
"""


def MakeList(value_list, prefix='', quoter=QuoteIfNecessary, initial_indent=0):
  """Construct from a list of values a string that can be assigned to a make
  variable.  This uses line continuations and limits line length to 80 chars.
  """
  if not value_list:
    return ''

  value_list = [quoter(prefix + l) for l in value_list]
  lines = []
  line = ' ' * initial_indent
  for value in value_list:
    if len(line) + len(value) >= 79:
      lines.append(line)
      line = ''
    elif line:
      line += ' '
    line += value
  lines.append(line)
  rtn = ' \\\n\t'.join(lines)
  return rtn.lstrip()


def WriteList(makefile, value_list, variable, prefix='', quoter=QuoteIfNecessary):
  values = MakeList(value_list, prefix, quoter, initial_indent=len(variable)+4)
  makefile.write("\n%s := %s\n" % (variable, values))


def WriteConfig(makefile, name, config, target_type):
  WriteList(makefile, config.get('defines', []), 'DEFS_%s' % name, '-D')
  WriteList(makefile, config.get('cflags', []), 'CPPFLAGS_%s' % name)
  WriteList(makefile, config.get('arflags', []), 'ARFLAGS_%s' % name)
  ldflags = config.get('ldflags', [])
  if target_type == 'shared_library':
    ldflags.insert(0, '-shared')
  WriteList(makefile, ldflags, 'LDFLAGS_%s' % name)

  include_dirs = config.get('include_dirs', [])
  include_dirs = ["-I%s" % i for i in include_dirs]
  common_flags = ['$(CPPFLAGS_%s)' % name, '$(DEFS_%s)' % name, '$(DEPFLAGS)']
  common_flags += include_dirs
  WriteList(makefile, common_flags + config.get('cflags_c', []), 'CFLAGS_%s' % name)
  WriteList(makefile, common_flags + config.get('cflags_cc', []), 'CXXFLAGS_%s' % name)


def WriteActions(makefile, actions, target_type):
  for action in actions:
    cmd = gyp.common.EncodePOSIXShellList(action['action'])
    makefile.write("\t%s\n" % cmd)
  if target_type == 'none':
    makefile.write("\ttouch $@\n")
  makefile.write("\n")


def WriteTarget(makefile, target_info):
  valid_conf = ' '.join(target_info.get('make_valid_configurations', []))
  if valid_conf:
    makefile.write("\nifneq (,$(findstring $(BUILDTYPE),%s))\n" % valid_conf)

  makefile.write('''
##
# Settings for the '%(target_name)s'
##
''' % target_info)

  sources = target_info.get('sources', [])
  exts = ['.cc', '.c', '.cxx', '.cpp']
  sources = [s for s in sources if os.path.splitext(s)[1] in exts]
  objects = [os.path.splitext(src)[0] for src in sources]
  objects = [obj + '.o' for obj in objects]

  target_name_var = target_info['target_name']
  target_name_var = target_name_var.replace('.', '_')

  for name, config in target_info['configurations'].items():
    name = target_name_var + '_' + name
    WriteConfig(makefile, name, config, target_info['type'])

  actions = target_info.get('actions', [])

  params = {
    'target': target_info['target_name'],
    'product': target_info['target_name'],
    'target_name_var': target_name_var,
  }

  if 'product_name' in target_info:
    params['product'] = target_info['product_name']

  if target_info['type'] == 'static_library':
    prefix = 'lib'
  elif target_info['type'] == 'shared_library':
    prefix = 'lib'
  else:
    prefix = ''

  if prefix and not params['product'].startswith(prefix):
    params['product'] = prefix + params['product']

  dirname = target_info.get('product_dir', '$(BUILDDIR)/$(BUILDTYPE)')
  params['product'] = os.path.join(dirname, params['product'])

  if target_info['type'] == 'none':
    params.update({
      'inputs': MakeList(actions[0]['inputs'])
    })
    makefile.write(none_section % params)
  else:
    builddir = '$(BUILDDIR)/$(BUILDTYPE)/obj_%s' % target_info['target_name']
    params.update({
      'sources': MakeList(sources),
      'libs': MakeList(target_info['libraries']),
      'objs': MakeList(["%s/%s" % (builddir, obj) for obj in objects]),
      'toolset': target_info['toolset']
    })

    makefile.write(target_section % params)
    if target_info['type'] == 'static_library':
      makefile.write(lib_section % params)
    else:
      makefile.write(link_section % params)

  WriteActions(makefile, actions, target_info['type'])
  if valid_conf:
    makefile.write('endif\n')


def GenerateOutput(target_list, target_dicts, data, params):
  """Main entry point for this generator.

  gyp will call this function.
  """
  options = params['options']
  makefilename = os.path.join(options.toplevel_dir, 'Makefile')
  makefile = open(makefilename, 'w')

  build_file, _, _ = gyp.common.ParseQualifiedTarget(target_list[0])
  make_global_settings = data[build_file].get('make_global_settings', [])
  settings_map = dict((key, value) for key, value in make_global_settings)

  target_info = target_dicts[target_list[0]]

  params = {
    'CC.target': GetEnvironFallback(['CC_target'], '$(CC)'),
    'AR.target': GetEnvironFallback(['AR_target'], '$(AR)'),
    'ARFLAGS.target': GetEnvironFallback(['ARFLAGS_target'], 'crs'),
    'CXX.target': GetEnvironFallback(['CXX_target'], '$(CXX)'),
    'LINK.target': GetEnvironFallback(['LINK_target'], '$(LINK)') ,

    'ARFLAGS.host': GetEnvironFallback(['ARFLAGS_host'], 'crs'),

    'default_config': target_info['default_configuration'],
    'all_configs': ' '.join(target_info['configurations'].keys()),
  }

  params.update(settings_map)
  makefile.write(preamble % params)

  for target_info in target_dicts.values():
    WriteTarget(makefile, target_info)

  makefile.write('''
# include (if they exists) the .d dependency files that the compiler generates
-include $(DEPFILES)

endif
''')

  makefile.close()
